home *** CD-ROM | disk | FTP | other *** search
/ Hot Super Models / Hot Super Models.iso / unix / x11 / xv200.tar / xv-2.00 / xvbutt.c < prev    next >
C/C++ Source or Header  |  1992-01-02  |  28KB  |  1,034 lines

  1. /* 
  2.  * xvbutt.c - regular, 'radio', 'checkbox', and 'menu' pushbuttons
  3.  *
  4.  * callable functions:
  5.  *
  6.  *   BTCreate()             -  create a button
  7.  *   BTSetActive()          -  change 'active' status of button
  8.  *   BTRedraw()             -  redraw button
  9.  *   BTTrack()              -  clicked in button.  track until mouse up
  10.  *
  11.  *   RBCreate()             -  create an RBUTT and append to supplied list
  12.  *   RBRedraw()             -  redraw one or all RBUTTs in a list
  13.  *   RBSelect()             -  change selected item in list of RBUTTs
  14.  *   RBWhich()              -  returns index of selected RBUTT in list
  15.  *   RBCount()              -  returns # of RBUTTs in list
  16.  *   RBSetActive()          -  sets active status of an RBUTT
  17.  *   RBClick()              -  finds clicked-on rb in a list
  18.  *   RBTrack()              -  tracks rb after click, until release
  19.  * 
  20.  *   CBCreate()             -  create a CBUTT (checkbox button)
  21.  *   CBRedraw()             -  redraw a CBUTT
  22.  *   CBSetActive()          -  change active status of a CBUTT
  23.  *   CBClick()              -  returns true if given CB was clicked on
  24.  *   CBTrack()              -  tracks CBUTT after click, until release
  25.  *
  26.  *   MBCreate()             -  create a MBUTT (menu button)
  27.  *   MBRedraw()             -  redraw a MBUTT
  28.  *   MBSetActive()          -  change active status of a MBUTT
  29.  *   MBClick()              -  returns true if given MB was clicked on
  30.  *   MBTrack()              -  tracks MBUTT after click, until release
  31.  */
  32.  
  33.  
  34. /*
  35.  * Copyright 1989, 1990, 1991, 1992 by John Bradley and
  36.  *                       The University of Pennsylvania
  37.  *
  38.  * Permission to use, copy, and distribute for non-commercial purposes,
  39.  * is hereby granted without fee, providing that the above copyright
  40.  * notice appear in all copies and that both the copyright notice and this
  41.  * permission notice appear in supporting documentation. 
  42.  *
  43.  * The software may be modified for your own purposes, but modified versions
  44.  * may not be distributed.
  45.  *
  46.  * This software is provided "as is" without any expressed or implied warranty.
  47.  *
  48.  * The author may be contacted via:
  49.  *    US Mail:   John Bradley
  50.  *               GRASP Lab, Room 301C
  51.  *               3401 Walnut St.  
  52.  *               Philadelphia, PA  19104
  53.  *
  54.  *    Phone:     (215) 898-8813
  55.  *    EMail:     bradley@cis.upenn.edu       
  56.  */
  57.  
  58.  
  59. #include "xv.h"
  60. #include "bitmaps.h"
  61.  
  62. static Pixmap cboard50 = (Pixmap) NULL;   /* 50% gray checkerboard */
  63.  
  64.  
  65. static int    rbpixmade = 0;
  66. static Pixmap rbon, rboff, rbon1, rboff1;
  67.  
  68. static int    cbpixmade = 0;
  69. static Pixmap cbon, cboff, cbon1, cboff1;
  70.  
  71. static int    mbpixmade = 0;
  72. static Pixmap mbchk;
  73.  
  74. #ifdef __STDC__
  75. static void drawRB(RBUTT *);
  76. #else
  77. static void drawRB();
  78. #endif
  79.  
  80.  
  81.  
  82. /******************* BUTT ROUTINES ************************/
  83.  
  84.  
  85.  
  86. /**********************************************/
  87. void BTCreate(bp,win,x,y,w,h,str,fg,bg)
  88. BUTT         *bp;
  89. Window        win;
  90. int           x,y,w,h;
  91. char         *str;
  92. unsigned long fg,bg;
  93. {
  94.   bp->win = win;
  95.   bp->x = x;  bp->y = y;  bp->w = w;  bp->h = h;
  96.   bp->str = str;
  97.   bp->fg = fg;  bp->bg = bg;
  98.   bp->lit = 0;
  99.   bp->active = 1;
  100.   bp->toggle = 0;
  101.   bp->pix = None;
  102.   bp->style = 0;
  103.   bp->fwidth = 3;
  104.  
  105.   if (!cboard50) {
  106.     cboard50 = XCreatePixmapFromBitmapData(theDisp, rootW, cboard50_bits,
  107.                cboard50_width, cboard50_height, 1, 0, 1);
  108.     if (!cboard50) FatalError("Unable to create cboard50 bitmap\n");
  109.   }
  110. }
  111.  
  112.  
  113.  
  114. /**********************************************/
  115. void BTSetActive(bp,act)
  116. BUTT         *bp;
  117. int           act;
  118. {
  119.   if (bp->active != act) {
  120.     bp->active = act;
  121.     BTRedraw(bp);
  122.   }
  123. }
  124.  
  125.  
  126.  
  127. /**********************************************/
  128. void BTRedraw(bp)
  129. BUTT *bp;
  130. {
  131.   int i,x,y,w,h,r,x1,y1;
  132.   XPoint tpts[10], bpts[10], ipts[5];
  133.  
  134.   x = bp->x;  y=bp->y;  w=bp->w;  h=bp->h;  r=bp->fwidth;
  135.  
  136.   if (!bp->active) bp->lit = 0;
  137.   if (bp->lit) {
  138.     r -= 1;
  139.     if (r<0) r = 0;
  140.   }
  141.  
  142.   /* set up 'ipts' */
  143.   ipts[0].x = x+r;        ipts[0].y = y+r;         /* topleft */
  144.   ipts[1].x = x+r;        ipts[1].y = y+h-r;       /* botleft */
  145.   ipts[2].x = x+w-r;      ipts[2].y = y+h-r;       /* botright */
  146.   ipts[3].x = x+w-r;      ipts[3].y = y+r;         /* topright */
  147.   ipts[4].x = ipts[0].x;  ipts[4].y = ipts[0].y;   /* close path */
  148.  
  149.   /* top left polygon */
  150.   tpts[0].x = x;            tpts[0].y = y;
  151.   tpts[1].x = x;            tpts[1].y = y+h;
  152.   tpts[2].x = ipts[1].x;    tpts[2].y = ipts[1].y;
  153.   tpts[3].x = ipts[0].x;    tpts[3].y = ipts[0].y;
  154.   tpts[4].x = ipts[3].x;    tpts[4].y = ipts[3].y;
  155.   tpts[5].x = x+w;          tpts[5].y = y;
  156.   tpts[6].x = x;            tpts[6].y = y;
  157.  
  158.   /* bot left polygon */
  159.   bpts[0].x = x;            bpts[0].y = y+h;
  160.   bpts[1].x = ipts[1].x;    bpts[1].y = ipts[1].y;
  161.   bpts[2].x = ipts[2].x;    bpts[2].y = ipts[2].y;
  162.   bpts[3].x = ipts[3].x;    bpts[3].y = ipts[3].y;
  163.   bpts[4].x = x+w;          bpts[4].y = y;
  164.   bpts[5].x = x+w;          bpts[5].y = y+h;
  165.   bpts[6].x = x;            bpts[6].y = y+h;
  166.  
  167.  
  168.   if (!ctrlColor) {
  169.     /* clear button and draw frame */
  170.     XSetForeground(theDisp, theGC, bp->bg);
  171.     XFillRectangle(theDisp, bp->win, theGC, x, y, w, h);
  172.     XSetForeground(theDisp, theGC, bp->fg);
  173.     XDrawRectangle(theDisp, bp->win, theGC, x, y, w, h);
  174.  
  175.     XSetForeground(theDisp, theGC, bp->fg);
  176.     XSetFillStyle(theDisp, theGC, FillStippled);
  177.     XSetStipple(theDisp, theGC, cboard50);
  178.     XFillPolygon(theDisp, bp->win, theGC, bpts, 7, Nonconvex, CoordModeOrigin);
  179.     XSetFillStyle(theDisp,theGC,FillSolid);
  180.  
  181.     XSetForeground(theDisp, theGC, bp->fg);
  182.     XDrawLines(theDisp, bp->win, theGC, ipts, 5, CoordModeOrigin);  /* inset */
  183.  
  184.     XDrawLine(theDisp, bp->win, theGC, x+1,   y+1,  ipts[0].x,ipts[0].y);
  185.     XDrawLine(theDisp, bp->win, theGC, x+1,   y+h-1,ipts[1].x,ipts[1].y);
  186.     XDrawLine(theDisp, bp->win, theGC, x+w-1, y+h-1,ipts[2].x,ipts[2].y);
  187.     XDrawLine(theDisp, bp->win, theGC, x+w-1, y+1,  ipts[3].x,ipts[3].y);
  188.  
  189.     if (bp->lit) {
  190.       XDrawRectangle(theDisp, bp->win, theGC, x+2, y+2, w-4, h-4);
  191.       XDrawRectangle(theDisp, bp->win, theGC, x+1, y+1, w-2, h-2);
  192.     }
  193.   }
  194.     
  195.   else {   /* ctrlColor */
  196.     XSetForeground(theDisp, theGC, bp->bg);
  197.     XFillRectangle(theDisp, bp->win, theGC, x+1, y+1, w-1, h-1);
  198.  
  199.     XSetForeground(theDisp, theGC, hicol);
  200.     for (i=1; i<=r; i++) {
  201.       XDrawLine(theDisp, bp->win, theGC, x+i, y+i, x+w, y+i);
  202.       XDrawLine(theDisp, bp->win, theGC, x+i, y+i, x+i, y+h);
  203.     }
  204.  
  205.     XSetForeground(theDisp, theGC, locol);
  206.     for (i=1; i<=r; i++) {
  207.       XDrawLine(theDisp, bp->win, theGC, x+i, y+h-i, x+w, y+h-i);
  208.       XDrawLine(theDisp, bp->win, theGC, x+w-i, y+h, x+w-i, y+i);
  209.     }
  210.  
  211.     XSetForeground(theDisp, theGC, bp->fg);
  212.     XDrawRectangle(theDisp, bp->win, theGC, x, y, w, h);
  213.  
  214.     if (bp->lit)
  215.       XDrawRectangle(theDisp, bp->win, theGC, x+1, y+1, w-2, h-2);
  216.   }
  217.     
  218.  
  219.  
  220.  
  221.   XSetForeground(theDisp, theGC, fg);
  222.  
  223.   if (bp->pix != None) {                    /* draw pixmap centered in butt */
  224.     x1 = x+(1+w-bp->pw)/2;
  225.     y1 = y+(1+h-bp->ph)/2;
  226.  
  227.     XSetBackground(theDisp, theGC, bg);
  228.     XCopyPlane(theDisp, bp->pix, bp->win, theGC, 0,0,bp->pw,bp->ph, x1,y1, 1);
  229.     if (!bp->active) DimRect(bp->win, x1,y1, bp->pw, bp->ph, bp->bg);
  230.   }
  231.  
  232.   else {                                    /* draw string centered in butt */
  233.     x1 = CENTERX(mfinfo, x + w/2, bp->str);
  234.     y1 = CENTERY(mfinfo, y + h/2);
  235.  
  236.     if (bp->active) {
  237.       XDrawString(theDisp, bp->win, theGC, x1,y1, bp->str, strlen(bp->str));
  238.     }
  239.     else {  /* stipple if not active */
  240.       XSetFillStyle(theDisp, theGC, FillStippled);
  241.       XSetStipple(theDisp, theGC, grayStip);
  242.       XDrawString(theDisp, bp->win, theGC, x1,y1, bp->str, strlen(bp->str));
  243.       XSetFillStyle(theDisp,theGC,FillSolid);
  244.     }
  245.   }
  246.  
  247. }
  248.  
  249.  
  250.  
  251. /**********************************************/
  252. int BTTrack(bp)
  253. BUTT *bp;
  254. {
  255.   /* called when we've gotten a click inside 'bp'.  returns 1 if button
  256.      was still selected lit when mouse was released. */
  257.  
  258.   Window       rW, cW;
  259.   int          x, y, rx, ry, rval, inval;
  260.   unsigned int mask;
  261.  
  262.   if (!bp->active) return 0;   /* inactive button */
  263.  
  264.   inval = bp->lit;
  265.   bp->lit = !bp->lit;
  266.  
  267.   BTRedraw(bp);  XFlush(theDisp);
  268.   Timer(120);  /* long enough for turn on to be visible */
  269.  
  270.   while (XQueryPointer(theDisp,bp->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  271.     if (!(mask & Button1Mask)) break;    /* button released */
  272.  
  273.     if (bp->lit==inval && PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) {
  274.       bp->lit = !inval;  BTRedraw(bp);  XFlush(theDisp);
  275.     }
  276.     
  277.     if (bp->lit!=inval && !PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) {
  278.       bp->lit = inval;  BTRedraw(bp);  XFlush(theDisp);
  279.     }
  280.   }
  281.  
  282.   rval = (bp->lit != inval);
  283.   
  284.   if (bp->lit && !bp->toggle) 
  285.     { bp->lit = 0;  BTRedraw(bp);  XFlush(theDisp); }
  286.  
  287.   return(rval);
  288. }
  289.  
  290.  
  291.  
  292.  
  293. /******************* RBUTT ROUTINES ************************/
  294.  
  295.  
  296.  
  297.  
  298. /***********************************************/
  299. RBUTT *RBCreate(rblist, win, x,y,str, fg, bg)
  300.       RBUTT        *rblist;
  301.       Window        win;
  302.       int           x,y;
  303.       char         *str;
  304.       unsigned long fg,bg;
  305. {
  306.   /* mallocs an RBUTT, fills in the fields, and appends it to rblist
  307.      if rblist is NULL, this is the first rb in the list.  It will
  308.      be made the 'selected' one 
  309.  
  310.      Note: no need to check return status.  It'll fatal error if it 
  311.      can't malloc */
  312.  
  313.   RBUTT *rb, *rbptr;
  314.  
  315.   rb = (RBUTT *) malloc(sizeof(RBUTT));
  316.   if (!rb) FatalError("couldn't malloc RBUTT");
  317.  
  318.   /* fill in the fields of the structure */
  319.   rb->win      = win;
  320.   rb->x        = x;
  321.   rb->y        = y;
  322.   rb->str      = str;
  323.   rb->selected = 0;
  324.   rb->active   = 1;
  325.   rb->next     = (RBUTT *) NULL;
  326.   rb->fg       = fg;
  327.   rb->bg       = bg;
  328.  
  329.   if (rblist) {            /* append to end of list */
  330.     rbptr = rblist;
  331.     while (rbptr->next) rbptr = rbptr->next;
  332.     rbptr->next = rb;
  333.   }
  334.   else {                   /* this is the first one in the list.  select it */
  335.     rb->selected = 1;
  336.   }
  337.  
  338.  
  339.   /* and, on an unrelated note, if the RB pixmaps haven't been created yet,
  340.      do so.  We'll be needing them, y'see... */
  341.  
  342.   if (!rbpixmade) {
  343.     rbon  = XCreatePixmapFromBitmapData(theDisp, rootW, rb_on_bits,
  344.          rb_on_width, rb_on_height, fg, bg, dispDEEP);
  345.  
  346.     rboff = XCreatePixmapFromBitmapData(theDisp, rootW, rb_off_bits,
  347.          rb_off_width, rb_off_height, fg, bg, dispDEEP);
  348.     rbon1 = XCreatePixmapFromBitmapData(theDisp, rootW, rb_on1_bits,
  349.          rb_on1_width, rb_on1_height, fg, bg, dispDEEP);
  350.     rboff1= XCreatePixmapFromBitmapData(theDisp, rootW, rb_off1_bits,
  351.          rb_off1_width, rb_off1_height, fg, bg, dispDEEP);
  352.  
  353.     rbpixmade = 1;
  354.   }
  355.  
  356.   return(rb);
  357. }
  358.   
  359.  
  360.  
  361.  
  362. /***********************************************/
  363. void RBRedraw(rblist, num)
  364. RBUTT *rblist;
  365. int    num;
  366. {
  367.   /* redraws the 'num-th' RB in the list.  if num < 0, redraws entire list */
  368.  
  369.   RBUTT *rb;
  370.   int    i;
  371.  
  372.   /* point 'rb' at the appropriate RBUTT, *if* we're not drawing entire list */
  373.   if (num>=0) {
  374.     i=0;  rb=rblist;
  375.     while (i!=num && rb) { rb = rb->next;  i++; }
  376.     if (!rb) return;                     /* num is out of range.  do nothing */
  377.     drawRB(rb);
  378.   }
  379.  
  380.   else {                                 /* draw entire list */
  381.     rb = rblist;
  382.     while (rb) {
  383.       drawRB(rb);
  384.       rb = rb->next;
  385.     }
  386.   }
  387. }
  388.  
  389.  
  390. static void drawRB(rb)
  391. RBUTT *rb;
  392. {
  393.   /* draws the rb being pointed at */
  394.  
  395.   if (!rb) return;  /* rb = NULL */
  396.  
  397.   XSetForeground(theDisp, theGC, rb->fg);
  398.   XSetBackground(theDisp, theGC, rb->bg);
  399.  
  400.   if (rb->selected) 
  401.     XCopyArea(theDisp, rbon, rb->win, theGC, 0, 0, rb_on_width, rb_on_height, 
  402.           rb->x, rb->y);
  403.   else
  404.     XCopyArea(theDisp, rboff, rb->win, theGC, 0, 0, rb_on_width, rb_on_height, 
  405.           rb->x, rb->y);
  406.  
  407.   XDrawString(theDisp, rb->win, theGC, rb->x+rb_on_width+4, 
  408.           rb->y+rb_on_height/2 - CHIGH/2 + ASCENT,rb->str,strlen(rb->str));
  409.  
  410.   if (!rb->active) {  /* if non-active, dim button and string */
  411.     DimRect(rb->win, rb->x, rb->y, rb_on_width, rb_on_height, rb->bg);
  412.     DimRect(rb->win, rb->x + rb_on_width+4, rb->y+rb_on_height/2 - CHIGH/2, 
  413.         StringWidth(rb->str),CHIGH, rb->bg);
  414.   }
  415. }
  416.  
  417.  
  418. /***********************************************/
  419. void RBSelect(rblist, n)
  420. RBUTT *rblist;
  421. int    n;
  422. {
  423.   RBUTT *rbold, *rb;
  424.   int    i;
  425.  
  426.   /* makes rb #n the selected rb in the list.  Does all redrawing.  Does
  427.      nothing if rb already selected */
  428.  
  429.   /* get pointers to the currently selected rb and the desired rb */
  430.   rbold = rblist;
  431.   while (rbold && !rbold->selected) rbold = rbold->next;
  432.   if (!rbold) return;    /* no currently selected item.  shouldn't happen */
  433.  
  434.   rb = rblist;  i=0;
  435.   while (rb && i!=n) {rb = rb->next;  i++; }
  436.   if (!rb) return;    /* 'n' is out of range */
  437.  
  438.  
  439.   if (rb == rbold) return;   /* 'n' is already selected.  do nothing */
  440.  
  441.   rbold->selected = 0;
  442.   rb->selected    = 1;
  443.   drawRB(rbold);
  444.   drawRB(rb);
  445. }
  446.  
  447.  
  448.           
  449. /***********************************************/
  450. int RBWhich(rblist)
  451. RBUTT *rblist;
  452. {
  453.   int i;
  454.  
  455.   /* returns index of currently selected rb.  if none, returns -1 */
  456.  
  457.   i = 0;
  458.   while (rblist && !rblist->selected) { rblist = rblist->next;  i++; }
  459.  
  460.   if (!rblist) return -1;             /* didn't find one */
  461.   return i;
  462. }
  463.  
  464.  
  465. /***********************************************/
  466. int RBCount(rblist)
  467. RBUTT *rblist;
  468. {
  469.   int i;
  470.  
  471.   /* returns # of rb's in the list */
  472.  
  473.   i = 0;
  474.   while (rblist) { rblist = rblist->next; i++; }
  475.   return i;
  476. }
  477.  
  478.  
  479. /***********************************************/
  480. void RBSetActive(rblist, n, act)
  481. RBUTT *rblist;
  482. int n,act;
  483. {
  484.   RBUTT *rb;
  485.   int    i;
  486.  
  487.   /* sets 'active' status of rb #n.  does redrawing */
  488.  
  489.   rb=rblist;  i=0;
  490.   while (rb && i!=n) { rb = rb->next; i++; }
  491.   if (!rb) return;                         /* n out of range.  do nothing */
  492.  
  493.   if (rb->active != act) {
  494.     rb->active = act;
  495.     drawRB(rb);
  496.   }
  497. }
  498.  
  499.  
  500. /***********************************************/
  501. int RBClick(rblist, mx, my)
  502. RBUTT *rblist;
  503. int    mx,my;
  504. {
  505.   int i;
  506.  
  507.   /* searches through rblist to see if mouse click at mx,my is in the
  508.      clickable region of any of the rb's.  If it finds one, it returns 
  509.      it's index in the list.  If not, returns -1 */
  510.  
  511.   i = 0;
  512.   while (rblist) {
  513.     if (PTINRECT(mx, my, rblist->x, rblist->y, rb_on_width, rb_on_height)) 
  514.       break;
  515.     rblist = rblist->next;
  516.     i++;
  517.   }
  518.  
  519.   if (!rblist) return -1;
  520.   return(i);
  521. }
  522.  
  523.  
  524. /***********************************************/
  525. int RBTrack(rblist, n)
  526. RBUTT *rblist;
  527. int    n;
  528. {
  529.   RBUTT       *rb;
  530.   Window       rW, cW;
  531.   int          i, x, y, rx, ry, lit, rv;
  532.   unsigned int mask;
  533.   Pixmap litpix, darkpix;
  534.  
  535.   /* returns '1' if selection changed */
  536.  
  537.   rb=rblist;  i=0;
  538.   while (rb && i!=n) { rb = rb->next; i++; }
  539.   if (!rb) return 0;                    /* n out of range */
  540.  
  541.   /* called once we've figured out that the mouse clicked in 'rb' */
  542.  
  543.   if (!rb->active) return 0;
  544.  
  545.   if (rb->selected) { litpix = rbon1;   darkpix = rbon; }
  546.                else { litpix = rboff1;  darkpix = rboff; }
  547.  
  548.   lit = 1;
  549.   XCopyArea(theDisp, litpix, rb->win, theGC, 0, 0, rb_on_width, rb_on_height, 
  550.           rb->x, rb->y);
  551.   XFlush(theDisp);
  552.   Timer(75);          /* give chance for 'turn on' to become visible */
  553.  
  554.   while (XQueryPointer(theDisp,rb->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  555.     if (!(mask & Button1Mask)) break;    /* button released */
  556.  
  557.     if (!lit && PTINRECT(x, y, rb->x, rb->y, rb_on_width, rb_on_height)) {
  558.       lit=1;
  559.       XCopyArea(theDisp, litpix, rb->win, theGC, 0,0,rb_on_width,rb_on_height, 
  560.           rb->x, rb->y);
  561.       XFlush(theDisp);
  562.     }
  563.     
  564.     if (lit && !PTINRECT(x, y, rb->x, rb->y, rb_on_width, rb_on_height)) {
  565.       lit=0;
  566.       XCopyArea(theDisp, darkpix,rb->win,theGC, 0,0,rb_on_width,rb_on_height, 
  567.           rb->x, rb->y);
  568.       XFlush(theDisp);
  569.     }
  570.   }
  571.  
  572.   rv = 0;
  573.  
  574.   if (lit) {
  575.     XCopyArea(theDisp, darkpix, rb->win, theGC, 0, 0, 
  576.           rb_on_width, rb_on_height, rb->x, rb->y);
  577.     if (RBWhich(rblist) != n) rv = 1;
  578.     RBSelect(rblist, n);
  579.   }
  580.  
  581.   XFlush(theDisp);
  582.   return rv;
  583. }
  584.  
  585.  
  586.  
  587.  
  588. /******************* CBUTT ROUTINES ************************/
  589.  
  590.  
  591.  
  592.  
  593. /***********************************************/
  594. void CBCreate(cb, win, x,y, str, fg, bg)
  595.       CBUTT        *cb;
  596.       Window        win;
  597.       int           x,y;
  598.       char         *str;
  599.       unsigned long fg,bg;
  600. {
  601.   /* fill in the fields of the structure */
  602.   cb->win      = win;
  603.   cb->x        = x;
  604.   cb->y        = y;
  605.   cb->str      = str;
  606.   cb->val      = 0;
  607.   cb->active   = 1;
  608.   cb->fg       = fg;
  609.   cb->bg       = bg;
  610.  
  611.   /* and, on an unrelated note, if the CB pixmaps haven't been created yet,
  612.      do so.  We'll be needing them, y'see... */
  613.  
  614.   if (!cbpixmade) {
  615.     cbon  = XCreatePixmapFromBitmapData(theDisp, rootW, cb_on_bits,
  616.          cb_on_width, cb_on_height, fg, bg, dispDEEP);
  617.  
  618.     cboff = XCreatePixmapFromBitmapData(theDisp, rootW, cb_off_bits,
  619.          cb_off_width, cb_off_height, fg, bg, dispDEEP);
  620.     cbon1 = XCreatePixmapFromBitmapData(theDisp, rootW, cb_on1_bits,
  621.          cb_on1_width, cb_on1_height, fg, bg, dispDEEP);
  622.     cboff1= XCreatePixmapFromBitmapData(theDisp, rootW, cb_off1_bits,
  623.          cb_off1_width, cb_off1_height, fg, bg, dispDEEP);
  624.  
  625.     cbpixmade = 1;
  626.   }
  627. }
  628.   
  629.  
  630.  
  631.  
  632. /***********************************************/
  633. void CBRedraw(cb)
  634. CBUTT *cb;
  635. {
  636.   /* draws the cb being pointed at */
  637.  
  638.   XSetForeground(theDisp, theGC, cb->fg);
  639.   XSetBackground(theDisp, theGC, cb->bg);
  640.  
  641.   if (cb->val) 
  642.     XCopyArea(theDisp, cbon, cb->win, theGC, 0, 0, cb_on_width, cb_on_height, 
  643.           cb->x, cb->y);
  644.   else
  645.     XCopyArea(theDisp, cboff, cb->win, theGC, 0, 0, cb_on_width, cb_on_height, 
  646.           cb->x, cb->y);
  647.  
  648.   XDrawString(theDisp, cb->win, theGC, cb->x+cb_on_width+4, 
  649.           cb->y+cb_on_height/2 - CHIGH/2 + ASCENT,cb->str,strlen(cb->str));
  650.  
  651.   if (!cb->active) {  /* if non-active, dim button and string */
  652.     DimRect(cb->win, cb->x, cb->y, cb_on_width, cb_on_height, cb->bg);
  653.     DimRect(cb->win, cb->x + cb_on_width+4, cb->y+cb_on_height/2 - CHIGH/2, 
  654.         StringWidth(cb->str),CHIGH, cb->bg);
  655.   }
  656. }
  657.  
  658.  
  659. /**********************************************/
  660. void CBSetActive(cb,act)
  661. CBUTT        *cb;
  662. int           act;
  663. {
  664.   if (cb->active != act) {
  665.     cb->active = act;
  666.     CBRedraw(cb);
  667.   }
  668. }
  669.  
  670.  
  671. /***********************************************/
  672. int CBClick(cb, mx, my)
  673. CBUTT *cb;
  674. int    mx,my;
  675. {
  676.   if (PTINRECT(mx, my, cb->x, cb->y, cb_on_width, cb_on_height)) return 1;
  677.   return 0;
  678. }
  679.  
  680.  
  681. /***********************************************/
  682. int CBTrack(cb)
  683. CBUTT *cb;
  684. {
  685.   Window       rW, cW;
  686.   int          x, y, rx, ry, lit;
  687.   unsigned int mask;
  688.   Pixmap litpix, darkpix;
  689.  
  690.   /* called once we've figured out that the mouse clicked in 'cb' */
  691.  
  692.   if (!cb->active) return 0;
  693.  
  694.   if (cb->val) { litpix = cbon1;   darkpix = cbon; }
  695.           else { litpix = cboff1;  darkpix = cboff; }
  696.  
  697.   lit = 1;
  698.   XCopyArea(theDisp, litpix, cb->win, theGC, 0, 0, cb_on_width, cb_on_height, 
  699.           cb->x, cb->y);
  700.   XFlush(theDisp);
  701.   Timer(75);          /* give chance for 'turn on' to become visible */
  702.  
  703.   while (XQueryPointer(theDisp,cb->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  704.     if (!(mask & Button1Mask)) break;    /* button released */
  705.  
  706.     if (!lit && PTINRECT(x, y, cb->x, cb->y, cb_on_width, cb_on_height)) {
  707.       lit=1;
  708.       XCopyArea(theDisp, litpix, cb->win, theGC, 0,0,cb_on_width,cb_on_height, 
  709.           cb->x, cb->y);
  710.       XFlush(theDisp);
  711.     }
  712.     
  713.     if (lit && !PTINRECT(x, y, cb->x, cb->y, cb_on_width, cb_on_height)) {
  714.       lit=0;
  715.       XCopyArea(theDisp, darkpix,cb->win,theGC, 0,0,cb_on_width,cb_on_height, 
  716.           cb->x, cb->y);
  717.       XFlush(theDisp);
  718.     }
  719.   }
  720.  
  721.   if (lit) {
  722.     XCopyArea(theDisp, darkpix, cb->win, theGC, 0, 0, 
  723.           cb_on_width, cb_on_height, cb->x, cb->y);
  724.     cb->val = !cb->val;
  725.     CBRedraw(cb);
  726.   }
  727.  
  728.   XFlush(theDisp);
  729.  
  730.   return(lit);
  731. }
  732.  
  733.  
  734.  
  735.  
  736. /******************* MBUTT ROUTINES ************************/
  737.  
  738.  
  739.  
  740.  
  741. /***********************************************/
  742. void MBCreate(mb, win, x,y,w,h, str, list, nlist, fg, bg)
  743.       MBUTT        *mb;
  744.       Window        win;
  745.       int           x,y,w,h;
  746.       char         *str;
  747.       char        **list;
  748.       int           nlist;
  749.       unsigned long fg,bg;
  750. {
  751.   XSetWindowAttributes xswa;
  752.   unsigned long        xswamask;
  753.  
  754.   if (!mbpixmade) {
  755.     mbchk = XCreatePixmapFromBitmapData(theDisp, rootW, mb_chk_bits,
  756.          mb_chk_width, mb_chk_height, fg, bg, dispDEEP);
  757.     mbpixmade = 1;
  758.   }
  759.  
  760.  
  761.   /* fill in the fields of the structure */
  762.   mb->win      = win;
  763.   mb->x        = x;
  764.   mb->y        = y;
  765.   mb->w        = w;
  766.   mb->h        = h;
  767.   mb->title    = str;
  768.   mb->selected = -1;
  769.   mb->active   = 1;
  770.   mb->list     = list;
  771.   mb->nlist    = nlist;
  772.   mb->fg       = fg;
  773.   mb->bg       = bg;
  774.  
  775.   mb->pix      = (Pixmap) NULL;
  776.   mb->pw = mb->ph = 0;
  777.  
  778.  
  779.   /* create popup window (it gets mapped, pos'd and sized later) */
  780.   xswa.background_pixel = bg;
  781.   xswa.border_pixel     = fg;
  782.   xswa.save_under       = True;
  783.   xswamask = CWBackPixel | CWBorderPixel | CWSaveUnder;
  784.  
  785.   mb->mwin = XCreateWindow(theDisp, mb->win, x, y, w, h, 
  786.             bwidth, CopyFromParent, CopyFromParent,
  787.             CopyFromParent, xswamask, &xswa);
  788.  
  789.   if (!mb->mwin) FatalError("can't create popup menu window!");
  790.  
  791.   XSelectInput(theDisp, mb->mwin, ExposureMask | VisibilityChangeMask);
  792.   XSetTransientForHint(theDisp, mb->mwin, mb->win);
  793. }
  794.   
  795.  
  796.  
  797.  
  798. /***********************************************/
  799. void MBRedraw(mb)
  800. MBUTT *mb;
  801. {
  802.   /* draws a menu button in it's normal state.  (When it's actively being
  803.      used (to select an item), all drawing is handled in MBTrack) */
  804.  
  805.   int x,y,w,h,i,r,x1,y1;
  806.  
  807.   r = 2;  /* amt of shadow */
  808.   x = mb->x;  y = mb->y;  w = mb->w;  h = mb->h;
  809.  
  810.   XSetForeground(theDisp, theGC, mb->bg);
  811.   XFillRectangle(theDisp, mb->win, theGC, x+1, y+1, w-1, h-1);
  812.  
  813.   XSetForeground(theDisp, theGC, mb->fg);
  814.   XDrawRectangle(theDisp, mb->win, theGC, x, y, w, h);
  815.  
  816.   /* draw shadow */
  817.   for (i=1; i<=r; i++) {
  818.     XDrawLine(theDisp, mb->win, theGC, x+r, y+h+i, x+w+i, y+h+i);
  819.     XDrawLine(theDisp, mb->win, theGC, x+w+i, y+h+i, x+w+i, y+r);
  820.   }
  821.  
  822.   if (mb->pix != None) {                    /* draw pixmap centered in butt */
  823.     x1 = x + (1+w - mb->pw)/2;
  824.     y1 = y + (1+h - mb->ph)/2;
  825.  
  826.     XSetForeground(theDisp, theGC, mb->fg);
  827.     XSetBackground(theDisp, theGC, mb->bg);
  828.     XCopyPlane(theDisp, mb->pix, mb->win, theGC, 0,0,mb->pw,mb->ph, x1,y1, 1);
  829.     if (!mb->active) DimRect(mb->win, x1,y1, mb->pw, mb->ph, mb->bg);
  830.   }
  831.  
  832.   else {                                    /* draw string centered in butt */
  833.     char *str;
  834.  
  835.     if (mb->title) str = mb->title;
  836.     else {
  837.       if (mb->selected>=0 && mb->selected<mb->nlist) 
  838.     str = mb->list[mb->selected];
  839.       else str = "";
  840.     }
  841.  
  842.     x1 = CENTERX(mfinfo, x + w/2, str);
  843.     y1 = CENTERY(mfinfo, y + h/2);
  844.  
  845.     if (mb->active) {
  846.       XDrawString(theDisp, mb->win, theGC, x1,y1, str, strlen(str));
  847.     }
  848.     else {  /* stipple if not active */
  849.       XSetFillStyle(theDisp, theGC, FillStippled);
  850.       XSetStipple(theDisp, theGC, grayStip);
  851.       XDrawString(theDisp, mb->win, theGC, x1,y1, str, strlen(str));
  852.       XSetFillStyle(theDisp,theGC,FillSolid);
  853.     }
  854.   }
  855. }
  856.  
  857.  
  858. /**********************************************/
  859. void MBSetActive(mb,act)
  860. MBUTT        *mb;
  861. int           act;
  862. {
  863.   if (mb->active != act) {
  864.     mb->active = act;
  865.     MBRedraw(mb);
  866.   }
  867. }
  868.  
  869.  
  870. /***********************************************/
  871. int MBClick(mb, mx, my)
  872. MBUTT *mb;
  873. int    mx,my;
  874. {
  875.   if (PTINRECT(mx, my, mb->x, mb->y, mb->w, mb->h)) return 1;
  876.   return 0;
  877. }
  878.  
  879.  
  880. /***********************************************/
  881. int MBTrack(mb)
  882. MBUTT *mb;
  883. {
  884.   Window       rW, cW, win;
  885.   int          i, x, y, rx, ry, extratop;
  886.   unsigned int mask;
  887.   int          mwide, mhigh, mx, my, j, lit, lastlit;
  888.   XSizeHints   hints;
  889.   XEvent       event;
  890.  
  891.  
  892.   /* called once we've figured out that the mouse clicked in 'mb'
  893.      returns '1' if selection changed */
  894.  
  895.   if (!mb->active || !mb->nlist) return 0;
  896.  
  897.   /* invert the button, for visual feedback */
  898.   XSetFunction(theDisp, theGC, GXinvert);
  899.   XSetPlaneMask(theDisp, theGC, mb->fg ^ mb->bg);
  900.   XFillRectangle(theDisp, mb->win, theGC, mb->x+1, mb->y+1, mb->w-1, mb->h-1);
  901.   XSetFunction(theDisp, theGC, GXcopy);
  902.   XSetPlaneMask(theDisp, theGC, ~0L);
  903.  
  904.   extratop = (mb->title) ? LINEHIGH+3 : 1-SPACING; /*add extra line for title*/
  905.  
  906.  
  907.   mwide = 1;                              /* compute maximum width */
  908.   for (i=0; i<mb->nlist; i++) {
  909.     j = StringWidth(mb->list[i]);
  910.     if (j > mwide) mwide = j;
  911.   }
  912.   mwide += 8;                             /* extra room at edges */
  913.   if (mb->selected >=0 && mb->title) mwide += 8;
  914.   if (mwide < (mb->w+1)) mwide = mb->w+1; /* at least as wide as button */
  915.     
  916.   mhigh = mb->nlist * LINEHIGH + 2 + extratop;
  917.  
  918.   mx = mb->x-1;  my = mb->y - 1;
  919.   if (mb->title && mwide > mb->w) mx -= ((mwide - mb->w)/2);
  920.  
  921. #ifdef FOO
  922.   /* XTranslateCoordinates(theDisp, mb->win, rootW, mx, my, &mx, &my, &cW); */
  923.  
  924.   /* make sure menu is fully on-screen */
  925.   mtx = 0;  mty = 0;
  926.   if (mx + mwide > dispWIDE) { mtx = dispWIDE - (mx + mwide);  mx += mtx; }
  927.   if (my + mhigh > dispHIGH) { mty = dispHIGH - (my + mhigh);  my += mty; }
  928.   if (mx<0) { mtx -= mx;  mx = 0; }
  929.   if (my<0) { mty -= my;  my = 0; }
  930. #endif
  931.  
  932.  
  933.   /* create/map window, and warp mouse if we had to move the window */
  934.   win = mb->mwin;
  935.   XMoveResizeWindow(theDisp, win, mx, my, mwide, mhigh);
  936.  
  937.   hints.width  = hints.min_width  = hints.max_width  = mwide;
  938.   hints.height = hints.min_height = hints.max_height = mhigh;
  939.   hints.x = mx;  hints.y = my;
  940.   hints.flags  |= (USSize | PMinSize | PMaxSize | PPosition);
  941.   XSetNormalHints(theDisp, win, &hints);
  942.  
  943.   XMapRaised(theDisp, win);
  944.  
  945.   /* wait for window to become mapped */
  946.   XWindowEvent(theDisp, win, VisibilityChangeMask, &event);
  947.  
  948.  
  949.   /* draw the menu */
  950.   XSetForeground(theDisp, theGC, mb->fg);
  951.   x = (mb->selected >= 0 && mb->title) ? 12 : 4;
  952.   if (mb->title) {                /* draw a title on this menu */
  953.     CenterString(win, mb->title, mwide/2-1, (extratop-2)/2);
  954.     XDrawLine(theDisp, win, theGC, 0, extratop-2, mwide, extratop-2);
  955.     XDrawLine(theDisp, win, theGC, 0, extratop,   mwide, extratop);
  956.   }
  957.  
  958.   y = ASCENT + SPACING + extratop;
  959.   for (i=0; i<mb->nlist; i++) {
  960.     if (i == mb->selected && mb->title) {
  961.       XCopyArea(theDisp, mbchk, win, theGC, 0, 0, mb_chk_width, mb_chk_height, 
  962.           x - 10, y - 8);
  963.     }
  964.       
  965.     XDrawString(theDisp, win, theGC, x, y, mb->list[i], strlen(mb->list[i]));
  966.     y += LINEHIGH;
  967.   }
  968.  
  969.   XFlush(theDisp);
  970.  
  971.  
  972.   /* track the mouse */
  973.   XSetFunction(theDisp, theGC, GXinvert);         /* go in to 'invert' mode */
  974.   XSetPlaneMask(theDisp, theGC, mb->fg ^ mb->bg);
  975.  
  976.   lit = lastlit = -1;
  977.   while (XQueryPointer(theDisp,win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  978.     if (!(mask & Button1Mask)) break;    /* button released */
  979.  
  980.     /* determine which choice the mouse is in.  -1 if none */
  981.     j = extratop+2;
  982.     if (x < 0 || x > mwide) lit = -1;
  983.     else {
  984.       for (i=0; i<mb->nlist; i++, j+=LINEHIGH) {
  985.     if (y>=j && y < j+LINEHIGH) { lit = i; break; }
  986.       }
  987.       if (i == mb->nlist) lit = -1;
  988.     }
  989.  
  990.     if (lit != lastlit) {
  991.       if (lit >= 0) {
  992.     y = extratop + 2 + lit*LINEHIGH;
  993.     XFillRectangle(theDisp, win, theGC, 0, y, mwide, LINEHIGH);
  994.       }
  995.       if (lastlit >= 0) {
  996.     y = extratop + 2 + lastlit*LINEHIGH;
  997.     XFillRectangle(theDisp, win, theGC, 0, y, mwide, LINEHIGH);
  998.       }
  999.       lastlit = lit;
  1000.     }
  1001.   }
  1002.  
  1003.   /* flash the selected choice, if any */
  1004.   if (lit >= 0) {
  1005.     y = extratop + 2 + lit*LINEHIGH;
  1006.     for (i=0; i<5; i++) {
  1007.       XFillRectangle(theDisp, win, theGC, 0, y, mwide, LINEHIGH);
  1008.       XFlush(theDisp);
  1009.       Timer(50);
  1010.     }
  1011.   }
  1012.  
  1013.   XSetFunction(theDisp, theGC, GXcopy);   /* back to 'normal' mode */
  1014.   XSetPlaneMask(theDisp, theGC, ~0L);
  1015.  
  1016.   /* could try eating all remaining events for 'win' before unmapping */
  1017.  
  1018.   XSync(theDisp, False);                  /* make sure 'map' has taken place */
  1019.   XUnmapWindow(theDisp, win);
  1020.  
  1021.   MBRedraw(mb);
  1022.  
  1023.   if (lit >= 0 && lit != mb->selected) {
  1024.     mb->selected = lit;
  1025.     return 1;
  1026.   }
  1027.  
  1028.   return 0;
  1029. }
  1030.  
  1031.  
  1032.  
  1033.  
  1034.